﻿#region Using directives

using System;
using System.IO;
using System.Xml.Serialization;
using System.ServiceModel;
using MCMLXVII.BackEnd.Services.Common.Messages;

#endregion

namespace MCMLXVII.Core.Common.BackEnd
{

    public enum ConfigurationTracking
    { 
        NoTrack = 0,
        ReloadOnChange = 1,
        FireNotifications = 2
    }

    public class Configuration<T>
    {
        private string _FilePath;
        private object lockObject = new object();
        private T _CachedConfiguration;
        private string _ConfigName = "";
        private ConfigurationTracking _ChangeManagementPolicy;
        private FileSystemWatcher FWatcher;

        public delegate void ConfigurationChanged(Configuration<T> Sender, string ConfigName);

        public Configuration(string ConfigName, string FilePath, ConfigurationTracking ChangeManegementPolicy)
        {
            _FilePath = FilePath;
            _ConfigName = ConfigName;
            _ChangeManagementPolicy = ChangeManegementPolicy;
            lock (lockObject)
            {
                LoadConfigFile();
            }
            StartChangeWatcher();
        }
        private void StartChangeWatcher()
        {
            if ((_ChangeManagementPolicy == ConfigurationTracking.ReloadOnChange) || (_ChangeManagementPolicy == ConfigurationTracking.FireNotifications))
            {

                string _Path;
                if (_FilePath.IndexOf("\\") > 0)
                {
                    _Path = _FilePath.Substring(0, _FilePath.LastIndexOf("\\"));
                }
                else
                {
                    _Path = AppDomain.CurrentDomain.BaseDirectory;
                }

                FWatcher = new FileSystemWatcher(_Path);
                FWatcher.NotifyFilter = NotifyFilters.LastWrite;
                FWatcher.Changed += new FileSystemEventHandler(OnChanged);
                FWatcher.EnableRaisingEvents = true;
            }
        }

        public Configuration(string ConfigName, string FilePath, ConfigurationTracking ChangeManegementPolicy, T PreCreatedConfig)
        {
            _FilePath = FilePath;
            _ConfigName = ConfigName;
            _ChangeManagementPolicy = ChangeManegementPolicy;
            _CachedConfiguration = PreCreatedConfig;
            lock (lockObject)
            {
                SaveConfigFile();
            }
            StartChangeWatcher();
        }

        public T Info
        {
            get
            {
                lock (lockObject)
                {
                    if (_CachedConfiguration == null)
                        LoadConfigFile();
                    if (_CachedConfiguration != null)
                        return _CachedConfiguration;
                    else
                        throw new FaultException<BackendFault>(new BackendFault(BackendFaulType.Technical,"Unable to Read Configuration File", _FilePath));
                }
            }

            set
            {
                lock (lockObject)
                {
                    _CachedConfiguration = value;
                }
            }
        }

        private void LoadConfigFile()
        {
            if (!File.Exists(_FilePath))
                throw new FaultException<BackendFault>(new BackendFault(BackendFaulType.Technical,"Unable to Read Configuration File", _FilePath));
            using (FileStream fs = new FileStream(_FilePath, FileMode.Open, FileAccess.Read))
            {
                    XmlSerializer Serializer = new System.Xml.Serialization.XmlSerializer(typeof(T));
                    _CachedConfiguration = (T)Serializer.Deserialize(fs);
                    fs.Close();
            }
        }

        public void SaveConfigFile()
        {
            if (_CachedConfiguration == null)
                throw new FaultException<BackendFault>(new BackendFault(BackendFaulType.Technical,"Unable to Write to Configuration File", _FilePath));
            try
            {
                using (FileStream fs = new FileStream(_FilePath, FileMode.Create, FileAccess.Write ))
                {
                    XmlSerializer Serializer = new System.Xml.Serialization.XmlSerializer(typeof(T));
                    Serializer.Serialize(fs, _CachedConfiguration);
                    fs.Close();
                }
            }
            catch //TODO:Identify exceptions and throw adecuate technical configuration exceptions
            {
                throw;

            }
        }

        public event ConfigurationChanged ConfigChanged;

        protected virtual void OnChanged(object sender, FileSystemEventArgs e)
        {
            if ((e.Name == _FilePath) || (e.FullPath == _FilePath))
            {
                lock (lockObject)
                {
                    try
                    {
                        LoadConfigFile();
                    }
                    catch
                    { 
                        // File Locked
                    }
                }
                if (ConfigChanged != null)
                {
                    if (_ChangeManagementPolicy == ConfigurationTracking.FireNotifications)
                        ConfigChanged(this, _ConfigName);
                }
            }
        }
    }
}
